"""
Copyright 2012 Wieser Dietmar, Margit Mutschlechner.

This file is part of Multimedia Social Networking Platform.

Multimedia Social Networking Platform is free software: 
you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Multimedia Social Networking Platform is distributed
in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Multimedia Social Networking Platform.  
If not, see <http://www.gnu.org/licenses/>.

"""

from threading import Thread
from msnp.service.utils import Hash, NetworkInfo
from msnp.service.network import NetworkController, AuthenticationValidator
from msnp.service.network.listening import Daemons
from msnp.gui import MySignal
from msnp.persistence import PersistenceController, XMLController
from msnp.service.network.networktimer import LowLevelPeerCrashTimer, ResendTimer, \
    KeepAliveTimer
from msnp.logger import Logger
from msnp.service.network.model.PeerModel import PeerInfo
from msnp.model import PTPModel, SettingsModel
from msnp.service.network.model.Model import ConnectionType


class InitializationThread(Thread):
    """Thread for initializing the peer.
    
    Attributes:
        __username: The user name as a String.
        __password: The password as a String.
        __isLogin: A boolean value, true if this is a login, false if its a register. Default True.
        __isNewNetwork: A boolean value, true if a new network should be started, false if it should connect to an existing network. Default False.
    """
    
    def __init__(self, username, password, isLogin=True, isNewNetwork=False):
        """the init method."""
        Thread.__init__(self)
        self.__username = username
        self.__password = password
        self.__isLogin = isLogin
        self.__isNewNetwork = isNewNetwork
        
    def run(self):
        """Initializes the peer.
        
        Starts the listening daemons, sets the own identity (username, password),
        initializes the dabases and calls either the function __createNewNetwork or __loginRegister
        depending of the values of isLogin and isNewNetwork.
        """
        settings = XMLController.readAllSettings()
        startPort = settings.getCurrentUserSettings().getStartPort()
        daemonPorts = Daemons.createDaemon(startPort);
        if daemonPorts != None:
            Logger.initLogFile(self.__username)
            self.__initDatabase()
            ResendTimer.startResendTimer()

            myIp = NetworkInfo.getPrivateIp()
            hPassword = Hash.hash(self.__password)
            myIdentity = PeerInfo(name=self.__username, pwd=hPassword, ip=myIp, ports=daemonPorts)
            NetworkController.getMySelf().setMyIdentity(myIdentity)
            NetworkController.getMySelf().setIsLogin(self.__isLogin)
                
            Logger.info(myIdentity.toString())
                
            #insert settings properties and go on
            currentUserSettings = settings.getCurrentUserSettings()
            startPort = str(myIdentity.getPortForTag(ConnectionType.MESSAGE_EXCHANGE))
            endPort = str(myIdentity.getPortForTag(ConnectionType.GROUP_MANAGEMENT))
            currentUserSettings.setStartPort(startPort)
            currentUserSettings.setEndPort(endPort)
            # remove all groups from settings if its a new network or a register
            if self.__isNewNetwork or self.__isLogin == False:
                currentUserSettings.setGroupSettingsList([])
            settings.setCurrentUserSettings(currentUserSettings)
            XMLController.storeSettings(settings)
            if self.__isNewNetwork:
                self.__createNewNetwork(myIdentity)
            else:
                XMLController.storeSettings(settings)
                self.__loginRegister()
        else:
            Logger.info("unable to start daemons: " + self.__username)
    
    def __createNewNetwork(self, myIdentity):
        """The peer creates an own network.
        
        The peer validates itself
        and emits an authFinishedSignal.
        
        Args:
            myIdentity: An object of type Peer identifying the peer.
        """
        
        Logger.logResult("----------------------------------------------------------------")
        Logger.info('I am the initial peer')
        NetworkController.getMySelf().setIsLowLevelPeer(False)
        NetworkController.getMySelf().setResponsibleSuperPeer(myIdentity)
        LowLevelPeerCrashTimer.startLowLevelPeerCrashTimer()
        #eventually stop, is running if I already tried to register or login and it failed
        KeepAliveTimer.stopKeepAliveTimer()
        
        validationType = AuthenticationValidator.validateAuthentication(myIdentity, False)
        if validationType != PTPModel.ReturnType.SUCCESSFUL:
            Logger.info('error during registration of initial peer. error code: ' + PTPModel.ReturnType.returnTypeToString(validationType))
            #LeaveRequest.requestFinish()
        MySignal.emitAuthFinishedSignal(validationType)
        
    def __loginRegister(self):
        """The peer tries to login or register to an already existing network.
        
        Sends a hello message with the value of the parameter __isLogin.
        """
        # eventually stop, is running if I already tried to create a new network and it failed
        LowLevelPeerCrashTimer.stopLowLevelPeerCrashTimer()
        #hello to network
        NetworkController.hello(self.__isLogin)
        
    def __initDatabase(self):
        """Initializes the databases.
        
        Reads the settings and initializes the peer-to-peer database,
        player database and media database.
        """
        settings = XMLController.readAllSettings()   
        msnpDBSettings = settings.getCurrentUserSettings().getMSNPDatabaseSettings()
        host = msnpDBSettings.getHost()
        user = msnpDBSettings.getUser()
        password = msnpDBSettings.getPassword()
        schema = msnpDBSettings.getSchema()
        PersistenceController.initPTPDatabase(host, user, password, schema)
        mediaDBSettings = settings.getCurrentUserSettings().getMediaDatabaseSettings()
        if mediaDBSettings.isComplete():
            host = mediaDBSettings.getHost()
            user = mediaDBSettings.getUser()
            password = mediaDBSettings.getPassword()
            schema = mediaDBSettings.getSchema()
            PersistenceController.initMediaDatabase(host, user, password, schema)
            
            # initialize player db only if media db is initialized because of triggers (need media db schema)
            playerDBSettings = settings.getCurrentUserSettings().getPlayerDatabaseSettings()
            if playerDBSettings.isComplete() and PersistenceController.testPlayerDBCredentials(playerDBSettings):
                PersistenceController.initPlayerDatabase(playerDBSettings, schema)
    
#this is called directly after the login
def initSettings(username):
    """Initializes the settings XML file. 
    
    Reads the settings file, if not present it generates new settings.
    Otherwise it updates the lastLoginUsername and writes the settings again to the XML file.
    
    Args:
        username: The user name as String.
    """
    #initialize settings
    settings = XMLController.readAllSettings()
    if settings == None:
        settings = SettingsModel.Settings()
        settings.initSettingsForNewUser(username)
    else:
        settings.setLastLoginUsername(username)
        currentUserSettings = settings.getCurrentUserSettings()
        if currentUserSettings == None:
            settings.initSettingsForNewUser(username)
    XMLController.storeSettings(settings)
            
def initialize(username, password, isLogin=True, isNewNetwork=False):
    """Initializes and starts an InitializationThread.
    
    Args:
        username: The user name as String.
        password: The password as String.
        isLogin: A boolean value, true if this is a login, false if its a register. Default True
        isNewNetwork: A boolean value, true if a new network should be started, false if it should connect to an existing network. Default False
    """
    initThread = InitializationThread(username, password, isLogin, isNewNetwork)
    initThread.start()
